package com.fwj.core.common.common;

import com.fwj.core.common.execption.CommonException;
import com.fwj.core.common.model.ResponseEnum;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: fwj
 * @Data: 2022/2/20 23:37
 * @Version 1.0
 * @Description:
 */


public class RedisUtil {

    //添加日志工厂
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    private static String config = "config";

    /**
     * Redis IP地址
     * */
    private static final String IP = PropertiesFileUtil.getInstance(config).get("redis.ip");

    /**
     * Redis 端口号
     */
    private static int PORT = PropertiesFileUtil.getInstance(config).getInt("redis.port");

    /**
     * Redis 密码
     */
    private static String PASSWORD = PropertiesFileUtil.getInstance(config).get("redis.password");

    /**
     * 可用连接实例的最大数目，默认值为8；
     * 如果赋值为 -1 则表示不限制连接数; 如果连接池已经分配了MAX_ACTIVE个jedis实例，则此时pool的状态为exhausted(耗尽)。
     */
    private static int MAX_ACTIVE = PropertiesFileUtil.getInstance(config).getInt("redis.max_active");

    /**
     * 连接池最多有多少个状态为IDLE(空闲的)的jedis实例,默认值和最大连接数相同。
     */
    private static int MAX_IDLE = PropertiesFileUtil.getInstance(config).getInt("redis.max_idle");

    /**
     * 等待可用连接的最大时间，单位 ms（毫秒），默认值为-1，表示永不超时。如果超过等待时间，则抛出JedisConnectionException
     */
    private static int MAX_WAIT = PropertiesFileUtil.getInstance(config).getInt("redis.max_wait");

    /**
     * 连接最大超时时间
     */
    private static int TIMEOUT = PropertiesFileUtil.getInstance(config).getInt("redis.timeout");

    /**
     * 在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
     */
    private static boolean TEST_ON_BORROW = false;

     //禁止指令重排
    private volatile static JedisPool jedisPool = null;

    private static Jedis getJedis(){
        //初始化redis线程池
        initRedisPool();
        Jedis jedis = null;
        try {
            if(null != jedisPool){
                jedis = jedisPool.getResource();
                if (!"${redis.password}".equals(PASSWORD)){
                    jedis.auth(PASSWORD);
                }
            }
        } catch (Exception e){
            LOGGER.error("Get Jedis Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        }
        return jedis;
    }

    //初始化Redis连接池
    private static void initRedisPool() {
        if (null == jedisPool){
            initJedisPool();
        }
    }

    /**
     * 初始化Jedis连接池,使用synchronized锁住方法，在该处使用synchronized的原因是可以避免getJedis锁住，即延迟锁，避免多线程下重复创建实例
     * */
    private static synchronized void initJedisPool() {
        //极个别情况下XX == null可能会出现空指针异常崩溃，鉴于此，最好还是常量放左边，变量放右边
        if (null == jedisPool){
            try {
                //构造Jedis配置并初始化连接池
                JedisPoolConfig config = new JedisPoolConfig();
                config.setMaxTotal(MAX_ACTIVE);
                config.setMaxIdle(MAX_IDLE);
                config.setMaxWaitMillis(MAX_WAIT);
                config.setTestOnBorrow(TEST_ON_BORROW);
                jedisPool = new JedisPool(config, IP, PORT, TIMEOUT);
            } catch (Exception e) {
                LOGGER.error("Init Jedis Error !");
                LOGGER.error(e.toString());
                throw new CommonException(ResponseEnum.SERVER_ERROR);
            }
        }
    }

    /**
     * 设置String对象
     * */
     public static void setString(String key, String value){
         Jedis jedis = getJedis();
         try {
             value = null == value ? "" : value;
             jedis.set(key, value);
         } catch (Exception e){
            LOGGER.error("Set Redis Value -> String Error !");
             LOGGER.error(e.toString());
             throw new CommonException(ResponseEnum.SERVER_ERROR);
         } finally {
             closeJedis(jedis);
         }
     }

    /**
     * 获取String对象
     * */
    public static String getString(String key){
        Jedis jedis = getJedis();
        String value = null;
        try {
            key = null == key ? "" : key;
            if (exists(key)){
                value = jedis.get(key);
            }
        } catch (Exception e){
            LOGGER.error("Set Redis Value -> String Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return value;
    }

    /**
     * 根据Key删除对象
     * */
    public static void delKey(String key){
        Jedis jedis = getJedis();
        try {
            key = null == key ? "" : key;
            if (exists(key)){
                jedis.del(key);
            }
        } catch (Exception e){
            LOGGER.error("Del Redis Key Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
    }

    /**
     * 检测数据存在于Redis
     * */
    public static boolean exists(String key){
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.exists(key);
            }
        } catch (Exception e) {
            LOGGER.error("Redis Data Exists -> Key Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return false;
    }

    /**
     * 设置List对象
     * */
    public static void setList(String key, List<?> list){
        Jedis jedis = getJedis();
        try {
            byte[] bytes = CommonUtils.isEmpty(list) ? "".getBytes() : serializeList(list);
            //避免因序列化导致jedis过期，所以做个检测
            if (null != jedis){
                jedis.set(key.getBytes(),bytes);
            }
        } catch (Exception e) {
            LOGGER.error("Set Redis Value -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
    }

    /**
     * 获取List对象
     * */
    public static List<?> getList(String key){
        Jedis jedis = getJedis();
        List<?> value = null;
        try {
            byte[] keyBytes = null == key ? "".getBytes() : key.getBytes();
            if (exists(key)){
                if (null != jedis){
                    byte[] valueBytes = jedis.get(keyBytes);
                    value = unSerializeList(valueBytes);
                }
            }
        } catch (Exception e){
            LOGGER.error("Get Redis Value -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return value;
    }

    private static void closeJedis(Jedis jedis) {
        if (null != jedis)
            jedis.close();
    }

    /**
     * 序列化 list 集合
     */
    public static byte[] serializeList(List<?> list){
        if (CommonUtils.isEmpty(list))
            return null;
        byte[] bytes = null;
        ObjectOutputStream oos = null;
        ByteArrayOutputStream bas = null;
        try {
            bas = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bas);
            for (Object obj : list) {
                oos.writeObject(obj);
            }
            bytes = bas.toByteArray();
        } catch (Exception e){
            LOGGER.error("Redis Serialize -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            close(oos);
            close(bas);
        }
        return bytes;
    }

    /**
     * 反序列化 list 集合
     */
    public static List<?> unSerializeList(byte[] bytes){
        List<Object> list = new ArrayList<>();
        ObjectInputStream ois = null;
        ByteArrayInputStream bas = null;
        try {
            bas = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bas);
            while (bas.available() > 0){
                Object obj = ois.readObject();
                if (null == obj)
                    break;
                list.add(obj);
            }
        } catch (Exception e){
            LOGGER.error("Redis UnSerialize -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            close(bas);
            close(ois);
        }
        return list;
    }

    /**
     * 关闭IO流对象
     */
    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error(e.toString());
                throw new CommonException(ResponseEnum.SERVER_ERROR);
            }
        }
    }

    /**
     * 设置Hash对象
     * */
    public static void setHash(String key, String field, String value){
        Jedis jedis = getJedis();
        try {
            if (null != jedis){
                jedis.hset(key,field,value);
            }
        } catch (Exception e) {
            LOGGER.error("Set Redis Value -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
    }

    /**
     * 获取单个Hash对象
     * */
    public static String getHash(String key, String field){
        Jedis jedis = getJedis();
        String value = null;
        try {
            if (hExists(key,field)){
                value = jedis.hget(key,field);
            }
        } catch (Exception e){
            LOGGER.error("Get Redis Value -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return value;
    }

    /**
     * 删除单个Hash对象
     * */
    public static long delHash(String key, String field){
        Jedis jedis = getJedis();
        Long res = 0L;
        try {
            if (hExists(key,field)){
                res = jedis.hdel(key,field);
            }
        } catch (Exception e){
            LOGGER.error("Del Redis Value -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return res;
    }

    public static boolean hExists(String key, String field) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hexists(key,field);
            }
        } catch (Exception e) {
            LOGGER.error("Redis Data Exists -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
        return false;
    }

    /**
     * 添加Redis Set集合元素(未完成)
     * */
    public static void setSet(String key, Object obj){
        Jedis jedis = getJedis();
        try {
            byte[] bytes = null == obj ? "".getBytes() : serialize(obj);
            if (null != jedis) {
                jedis.sadd(key.getBytes(),bytes);
            }
        } catch (Exception e) {
            LOGGER.error("Redis Data Exists -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
    }

    /**
     * 获取Redis Set集合(未完成)
     * */
    public static void getSet(String key,String value){
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                jedis.getSet(key.getBytes(),value.getBytes());
            }
        } catch (Exception e) {
            LOGGER.error("Redis Data Exists -> Hash Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            closeJedis(jedis);
        }
    }

    /**
     * 序列化Object
     */
    private static byte[] serialize(Object obj) {
        if (CommonUtils.isEmpty(obj))
            return null;
        byte[] bytes = null;
        ObjectOutputStream oos = null;
        ByteArrayOutputStream bas = null;
        try {
            bas = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bas);
            oos.writeObject(obj);
            bytes = bas.toByteArray();
        } catch (Exception e){
            LOGGER.error("Redis Serialize -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            close(oos);
            close(bas);
        }
        return bytes;
    }

    /**
     * 反序列化Object
     */
    public static Object unSerialize(byte[] bytes){
        Object obj = null;
        ObjectInputStream ois = null;
        ByteArrayInputStream bas = null;
        try {
            bas = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bas);
            obj = ois.readObject();
        } catch (Exception e){
            LOGGER.error("Redis UnSerialize -> List Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            close(bas);
            close(ois);
        }
        return obj;
    }

    /**
     * 设置 String 过期时间
     *
     * @param key
     * @param value
     * @param seconds 以秒为单位
     */
    public static void setTimeOut(String key, String value, int seconds) {
        Jedis jedis = getJedis();
        try {
            value = StringUtils.isBlank(value) ? "" : value;
            if (jedis != null) {
                jedis.setex(key, seconds, value);
            }
        } catch (Exception e) {
            LOGGER.error("Set Redis Value And TimeOut -> String Error !");
            LOGGER.error(e.toString());
            throw new CommonException(ResponseEnum.SERVER_ERROR);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }



}
